/******************************************************************************

Welcome to GDB Online.
GDB online is an online compiler and debugger tool for C, C++, Python, PHP, Ruby, 
C#, VB, Perl, Swift, Prolog, Javascript, Pascal, HTML, CSS, JS
Code, Compile, Run and Debug online from anywhere in world.

*******************************************************************************/
#include <stdio.h>
#include <vector>
#include <list>
#include <iostream>



// 模板模板参数【Tempalte Template Parameters】
// 就是模板参数本身成为模板的意思
// 强调一个名称问题
// 1）int : 简单类型、内部类型，其他的比如double，float等这些都是语言内置的简单类型
// 2）vector,list ：是c++标准库中的容器，类模板/类名，
// 使用的时候 vector<int>/list<int>就属于类模板被实例化了的产物，被成为类型（类类型）

// 需求：
// 创建一个类模板，MyClass, 成员变量myc是一个容器
// 我们希望在类模板实例化的时候，能通过模板参数，把myc是一个什么种类的容器指定出来
// 同时还能指定容器里的元素类型

// MyClass<int, vector> myvector;   // int是容器中的元素类型，vector是容器类型
// MyClass<double, list> mylist;   // double是容器中的元素类型，list是容器类型

// 模板参数：
// 1）类型模板参数
// 2）非类型模板参数
// 3）模板模板参数

// 模板模板参数的写法(注意，下面这里只有Container是模板模板参数，这里是为写完整，才把前面的给多加点东西)
// template<typename T, template<class> class Container = std::vector>
// template<typename T, template<typename U> typename Container = std::vector>
// 总而言之：模板模板参数，就是一个模板，模板能怎么写，他这里就能怎么写（只是在模板声明后面多了一个class或者typename）
// typename和class在模板里也都是可以混用的，意思是一样的
// 注意，模板模板参数里有几个地方可以省略不写
// 1）比如上面的模板模板参数里的U，这个U就是[模板模板参数]的[类型模板参数],
// 这个U因为在这里对于Container没有什么用，所以可以省略。
// 还有一点，这个U就算你写出来了，你也不能在类模板中进行使用，使用的话编译器会报错
// 这个U一般只能用在container的其他参数的声明中
// template<template<class U, U* point> class C>
// 而且说白了，这个U，只能用在模板模板参数的模板参数声明<>中
// U： [模板模板参数]的[模板参数]
// 2）Container这个模板模板参数名，如果在类模板中没有被使用，那么它也可以被省略不写
// 这样就出现了typename或者class后面直接接 = 的写法
// template<typename T, template<class> class = std::vector>


// 共用体模板(联合模板)【Union Templates】
// union：联合（共用体）他和结构（struct）有写类似的地方，
// 只不过共用体这种东西是把成员都放到同一段内存单元
// 可以把联合（共用体）理解成一种类类型（不要理解成类），联合也支持模板化


namespace _nmsp1
{
    // T 是类型模板参数，代表容器myc中的元素类型
    // Container 虽然也用typename修饰，但是实际上Container代表的不是一个类型（不能是一个类型模板参数）
    // 因为根据需求，Container这里应该代表的是一个类模板（类型）
    // template<typename T, typename Container = std::vector>
    // 所以如果要把Container这种类模板作为模板参数传递到类模板中，Container就不能是普通的类型模板参数
    // 而应该作为模板模板参数传递进去（表示Container本身又是一个模板）
    // 模板模板参数的语法格式 template<class> class C
    template<typename T, template<class> class Container = std::vector>
    class MyClass
    {
    public:
        Container<T> myc;
        
    public:
        MyClass() {}  // 构造函数
        
        void insert(T arg, size_t size)  // 普通成员函数,往容器中增加szie个元素
        {
            for(int i = 0; i < size; ++i)
            {
                myc.push_back(i + arg);
                // 这行代码是否正确，取决于实际实例化的时候，传递进来的容器的类型支不支持push_back()
            }
        }
        
        void display();
        // {
        //     for(auto iter = myc.begin(); iter!=myc.end();++iter)
        //     {
        //         std::cout << "打印：" << iter << std::endl;
        //     }
        // }
    };
    
    template<template<class> class Container>
    class MyClass<std::string, Container>
    {
    public:
        Container<std::string> myc;
        
    public:
        
        void insert(std::string arg, size_t size)  // 普通成员函数,往容器中增加szie个元素
        {
            for(int i = 0; i < size; ++i)
            {
                myc.push_back(arg.append(i, 'K'));
                // 这行代码是否正确，取决于实际实例化的时候，传递进来的容器的类型支不支持push_back()
            }
        }
        void display()
        {
            for(auto iter = myc.begin(); iter!=myc.end(); ++iter)
            {
                std::cout << "打印：" << *iter << std::endl;
            }
        }
    };
    
    // 类外实现（注意不能再类外给模板参数进行默认值赋值，不然编译会报错）
    // template<typename T, template<class>class Container = std::vector>
    template<typename T, template<class>class Container>
    void MyClass<T, Container>::display()
    {
        for(auto iter = myc.begin(); iter!=myc.end(); ++iter)
        {
            std::cout << "打印：" << *iter << std::endl;
        }
    }
    
    
    template<template<class U, U* point> class C>
    class MyClass2
    {
    public:
        // C<U*> myConP; // 错误，不可以在这里使用U
        // U* PIU; // 错误，不可以在这里使用U
        // 也就是说白了，这个U，只能用在模板模板参数的模板参数声明<>中
        // U： [模板模板参数]的[模板参数]
    };
    
    template<typename T, template<class> class = std::list>
    class MyClass3
    {
    public:
        
    };
    
    
    void func()
    {
        MyClass<int, std::vector> myVectorInt;
        MyClass<std::string, std::list> myListString;
        
        myVectorInt.insert(10, 10);
        myVectorInt.display();
        // 打印：10
        // 打印：11
        // 打印：12
        // 打印：13
        // 打印：14
        // 打印：15
        // 打印：16
        // 打印：17
        // 打印：18
        // 打印：19
        
        myListString.insert("test字符串", 8);
        myListString.display();
        // 打印：test字符串
        // 打印：test字符串K
        // 打印：test字符串KKK
        // 打印：test字符串KKKKKK
        // 打印：test字符串KKKKKKKKKK
        // 打印：test字符串KKKKKKKKKKKKKKK
        // 打印：test字符串KKKKKKKKKKKKKKKKKKKKK
        // 打印：test字符串KKKKKKKKKKKKKKKKKKKKKKKKKKKK
        
    }
}

namespace _nmsp2
{
    // 共用体模板
    template<typename T, typename U>
    union MyUnion
    {
        T carNum;           // 车编号
        U carType;          // 车类型
        U carName;          // 车名
    };
    
    
    
    void func()
    {
        // MyUnion<int, std::string> myu;
        // myu.carNum = 100;
        // myu.carType = "C1";
        // myu.carName = "biekeweilang";
        // c++中的union中数据成员是不允许有构造函数的
        // 所以上面这个在联合模板里传递string类型，就会编译不过
        
        MyUnion<int, const char*> myu;
        myu.carNum = 100;
        myu.carType = "C1";
        myu.carName = "biekeweilang";
        
        std::cout << "车编号" << myu.carNum << std::endl;
        std::cout << "车类型" << myu.carType << std::endl;
        std::cout << "车名" << myu.carName << std::endl;
        // 车编号-367742951
        // 车类型biekeweilang
        // 车名biekeweilang
    }
}

int main()
{
    // _nmsp1::func();
    _nmsp2::func();

    return 0;
}

